from byte_buffer import IByteBuffer


class ByteBufferMxl(IByteBuffer):
    RAPID_GROWTH_LIMIT = 1 << 20
    SLOW_GROWTH_STEP = 1 << 10
    DEFAULT_SIZE = 16

    def __init__(self, size: int = 16):
        if not isinstance(size, int):
            raise TypeError(f'ByteBuffer init size must be int, can not be {type(size)}')

        self.__capacity = self._calculate_alignment(size)
        self.__array = bytearray(self.__capacity)
        self.__view = memoryview(self.__array)
        self.__writer_index = 0
        self.__reader_index = 0

    def _calculate_alignment(self, value):
        if value <= self.DEFAULT_SIZE:
            return self.DEFAULT_SIZE
        elif value < self.RAPID_GROWTH_LIMIT:
            align = 1 << (value.bit_length() - 1)
            return value if value == align else align << 1
        else:
            align = value & ~(self.SLOW_GROWTH_STEP - 1)
            return value if value == align else align + self.SLOW_GROWTH_STEP

    def _discard_read_bytes(self):
        if self.__reader_index > 0:
            length = self.readable_bytes()
            self.__array[0:length] = self.__array[self.__reader_index:self.__writer_index]

            self.__reader_index = 0
            self.__writer_index = length

    def __adjust_capacity(self, size):
        if not isinstance(size, int):
            raise TypeError(f'ByteBuffer adjust size must be int, can not be {type(size)}')
        if size <= self.__capacity:
            raise ValueError(f'The adjust capacity ({size}) must be larger than current capacity({self.__capacity})')

        new_array = bytearray(size)
        new_array[0:self.readable_bytes()] = self.__array[self.__reader_index:self.__writer_index]
        self.__array = new_array
        self.__view = memoryview(self.__array)
        self.__capacity = size
        self.__writer_index -= self.__reader_index
        self.__reader_index = 0

    def write_bytes(self, bytes_data: (bytes | bytearray)):
        if not (isinstance(bytes_data, bytes) or isinstance(bytes_data, bytearray)):
            raise TypeError(f'Write bytes_data type({type(bytes_data)}) must be bytes/bytearray')

        data_len = len(bytes_data)
        if data_len < (self.__capacity - self.__writer_index):
            pass
        elif data_len <= self.writeable_bytes():
            self._discard_read_bytes()
        else:
            self.__adjust_capacity(self._calculate_alignment(self.readable_bytes() + data_len))

        self.__array[self.__writer_index:(self.__writer_index + data_len)] = bytes_data
        self.__writer_index += data_len
        return data_len

    def read_bytes(self, length):
        if not isinstance(length, int):
            raise TypeError(f'Read length must be int, can not be {type(length)}')
        if length < 0:
            raise ValueError(f'Read length({length}) must be >= 0')

        start = self.__reader_index
        end = self.__reader_index + length if length <= self.readable_bytes() else self.__writer_index
        self.__reader_index = end
        return self.__array[start:end]

    def memoryview(self):
        return self.__view[self.__reader_index:self.__writer_index]

    def skip_bytes(self, length):
        if not isinstance(length, int):
            raise TypeError(f'Skip length must be int, can not be {type(length)}')
        if length < 0:
            raise ValueError(f'Skip length({length}) must be >= 0')

        content_len = self.readable_bytes()
        length = length if length <= content_len else content_len
        self.__reader_index += length
        return length

    def reader_index(self):
        return self.__reader_index

    def writer_index(self):
        return self.__writer_index

    def writeable_bytes(self):
        return self.__capacity - self.__writer_index + self.__reader_index

    def readable_bytes(self):
        return self.__writer_index - self.__reader_index

    def is_readable(self):
        return self.__writer_index > self.__reader_index

    def is_readable_size(self, size):
        return self.is_readable() and self.readable_bytes() >= size

    def capacity(self):
        return self.__capacity

    def clear(self):
        self.__reader_index = 0
        self.__writer_index = 0

    def is_writable(self):
        return True

    def is_writable_size(self, size):
        return True

    def writable_bytes(self):
        return self.capacity() - self.readable_bytes()

    def _str_internal(self):
        return (f"capacity:{self.__capacity}, read_index:{self.__reader_index}, write_index:{self.__writer_index},"
                f"data len:{self.readable_bytes()}")

    def __str__(self):
        self._str_internal()
